Skip to content

Drop Babel-standalone from component pages (~600 KB per load)#3

Merged
aksOps merged 1 commit intomainfrom
feat/drop-babel-standalone
Apr 29, 2026
Merged

Drop Babel-standalone from component pages (~600 KB per load)#3
aksOps merged 1 commit intomainfrom
feat/drop-babel-standalone

Conversation

@aksOps
Copy link
Copy Markdown
Contributor

@aksOps aksOps commented Apr 28, 2026

Summary

Stacks on top of #2. Every component and chart docs page was loading @babel/standalone@7.29.0/babel.min.js (~600 KB) from unpkg purely to transform JSX strings that are already known at build time. This PR pre-transforms them with esbuild during build:site and drops the in-browser Babel pass entirely.

What changed

  • New compileDemo(jsxCode) in scripts/build-docs.mjs uses esbuild.transformSync (already a dev-dep, no new packages) with the classic JSX runtime — jsxFactory: "React.createElement", jsxFragment: "React.Fragment". Both names are bound in the runner factory via var { React, …, createRoot } = R, so the transformed JS evaluates as-is.
  • renderComponentPage now passes the compiled JS string to runExample / runChartExample. The displayed code panel and copy-button still receive the original JSX via DEMO_CODES — same UX, same source-of-truth.
  • renderRunner() dropped the Babel.transform pass and the if (!global.Babel) guard. The per-page readiness gate switched from window.RCS && window.Babel to just the bundle global the page needs (window.RCS or window.RCSCharts).
  • The <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js"> tag was removed from the component-page tail.

What this does NOT touch

  • /docs/playground/ still loads Babel-standalone — it has to, since it transforms whatever JSX the user types.
  • ui_kits/{app,marketing,docs}/index.html sample apps each ship their own Babel-standalone for their internal renders. Self-contained demo apps; out of scope.
  • The cytoscape peer dep contains an internal "@babel/helpers - typeof" comment string inside rcs-charts.iife.js. That's a substring match, not a runtime Babel load.

Verification

$ pnpm typecheck                # clean
$ pnpm test -- tests/unit/Sparkline.test.tsx  # 4/4
$ pnpm run build:site
  rcs.iife.js         236.5 KB  (unchanged)
  rcs-charts.iife.js  793.9 KB  (unchanged)
  rcs-charts.iife.css   1.6 KB  (unchanged)
  58 component pages, foundations, playground, etc.

Spot checks on the generated _site/:

  • /docs/Button/index.html → contains runExample("/* @__PURE__ */ React.createElement(Button, null)", "demo-render-0"). No <script src="…@babel/standalone…">.
  • /docs/Chart/index.html → contains runChartExample("/* @__PURE__ */ React.createElement(\n Chart,\n {\n timestamps: …", …). No <script src="…@babel/standalone…">.
  • runner.js → 4.2 KB → 2.7 KB.
  • grep -rln '@babel/standalone\|babel\.min\.js' _site/ → returns only the playground and the 3 ui_kits demo apps (expected).

Per-page transfer impact

Each of the 58 component / chart docs pages no longer fetches ~600 KB of Babel-standalone on first load and no longer runs an in-browser JSX compile pass. The compiled React.createElement(...) strings inline into each page tail — Button page grew from ~17 KB to ~20 KB, but the net is -580 KB per load.

Test plan

  • pnpm typecheck
  • Unit tests touching chart code still pass
  • pnpm run build:site clean
  • Generated HTML spot-checks (Button, Chart, runner.js)
  • Pages workflow green on push (CI)
  • E2E workflow green on push (CI) — Playwright will exercise the pre-transformed demo pipeline
  • Manual: open pnpm run serve:site, confirm Button + Chart + Sparkline pages render the live demo and the toggle still shows JSX

Note on PR base

This PR targets feat/react-only-docs (the base PR #2), not main. Once #2 lands, GitHub will retarget this to main automatically.

🤖 Generated with Claude Code

Component and chart docs pages were each pulling
`@babel/standalone@7.29.0/babel.min.js` from unpkg (~600 KB) just to
transform a handful of JSX strings already known at build time. The
@example blocks in src/components.ts and the chart demo literals are
all static — the browser was redoing work the build pipeline could
trivially do once.

Approach:

- New `compileDemo(jsxCode)` helper in build-docs.mjs uses esbuild's
  transformSync (already a dev-dep for the IIFE bundle) with the
  classic JSX runtime targeting `React.createElement` /
  `React.Fragment`. Both identifiers are bound in the runner factory
  via `var { React, Button, ..., createRoot } = R`, so the
  transformed JS evaluates without any further setup.
- renderComponentPage now feeds runFn(...) the *compiled* string per
  demo. The visible code panel and copy-button still receive the
  original JSX via DEMO_CODES — same UX, same source-of-truth.
- runner.js dropped the Babel.transform pass and its readiness check.
  The two run* helpers now eval the pre-transformed factory directly.
  The per-page readiness gate switched from `window.RCS && window.Babel`
  to just the bundle global appropriate for the page (window.RCS or
  window.RCSCharts), since there is no JSX compiler to wait on.
- The `<script src="…/@babel/standalone…">` tag was removed from the
  component-page tail.

Scope notes:

- The editable playground (/docs/playground/) still loads
  Babel-standalone — it transforms whatever JSX the user types. Out
  of scope to remove.
- The ui_kits/{app,marketing,docs}/index.html demo apps each load
  their own Babel-standalone for their internal renders. Those are
  self-contained sample apps, not part of the docs surface — also out
  of scope.
- Substring "babel" appears once inside rcs-charts.iife.js but only
  as a transitive comment from a chart peer dep (`@babel/helpers -
  typeof`) — not a runtime Babel load.

Verified: pnpm typecheck clean, Sparkline unit tests still pass,
pnpm run build:site succeeds, both bundles emit at unchanged sizes.
Spot-checked HTML: /docs/Button/index.html and /docs/Chart/index.html
contain the compiled `React.createElement(...)` strings and no
`@babel/standalone` script tag; runner.js shrank from ~4.2 KB to
~2.7 KB. `grep -rln '@babel/standalone\|babel\.min\.js' _site/` now
returns only the playground page and the three sample apps.

Per-page weight: each component/chart page no longer transfers
~600 KB of Babel-standalone or runs an in-browser JSX compile pass
on load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@aksOps aksOps force-pushed the feat/drop-babel-standalone branch from 7f8c1b5 to ff5ebd7 Compare April 28, 2026 16:36
@aksOps aksOps changed the base branch from feat/react-only-docs to main April 28, 2026 16:36
@aksOps aksOps merged commit 0db23a4 into main Apr 29, 2026
6 checks passed
@aksOps aksOps deleted the feat/drop-babel-standalone branch May 1, 2026 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant